﻿using Castle.DynamicProxy;
using Core.Proxy.Attributes.InterceptorAttributes;
using Core.Proxy.Utils;
using Core.CacheService.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using AutoRegistDependency.Abstract;
using AutoRegistDependency.Attributes;

namespace Core.Proxy.Interceptor.DataCache
{
	[Component]
	public class DataCacheInterceptor : AbstractInterceptor
	{
		private readonly static object locker = new object();
		private readonly ICache cache;
		public DataCacheInterceptor(ICache cache,IEnumerable<Assembly> dlls) : base(dlls)
		{
			this.cache = cache;
		}

		public override IEnumerable<Type> GetInterceptorTypes()
		{
			return dlls.SelectMany(t => t.GetTypes())
				.Where(a => !string.IsNullOrEmpty(a.Namespace) && (a.Namespace.StartsWith("Services") || a.Namespace.StartsWith("Repository")));
		}

		public override void Intercept(IInvocation invocation)
		{
			this.ToInterceptor().Intercept(invocation);
		}

		public override void InterceptAsynchronous(IInvocation invocation)
		{
			invocation.ReturnValue = InternalInterceptAsynchronous(invocation);
		}

		public override void InterceptAsynchronous<TResult>(IInvocation invocation)
		{
			invocation.ReturnValue = InternalInterceptAsynchronous<TResult>(invocation);
		}

		public override void InterceptSynchronous(IInvocation invocation)
		{
			var deleteAttr = (DeleteCacheAttribute)invocation.MethodInvocationTarget.GetCustomAttributes(true).Where(t => t.GetType() == typeof(DeleteCacheAttribute)).FirstOrDefault();
			var cacheAttr = (ReturnValueCacheAttribute)invocation.MethodInvocationTarget.GetCustomAttributes(true).Where(t => t.GetType() == typeof(ReturnValueCacheAttribute)).FirstOrDefault();
			if (deleteAttr != null)
			{
				string deleteKey = AutoFacHelper.GetKey(invocation, deleteAttr);
				cache.DeleteKey(deleteKey);
			}
			if (cacheAttr != null)
			{
				string cacheKey = AutoFacHelper.GetKey(invocation, cacheAttr);
				object cacheValue = cache.GetValue(cacheKey, invocation.MethodInvocationTarget.ReturnType);
				object defaultValue = AutoFacHelper.GetDefaultValue(invocation.MethodInvocationTarget.ReturnType);
				if (Equals(cacheValue, defaultValue))
				{
					lock (locker)
					{
						invocation.Proceed();
						cache.SetValue(cacheKey, invocation.ReturnValue, cacheAttr.ExpireMillisecond);
					}
				}
				invocation.ReturnValue = cache.GetValue(cacheKey, invocation.MethodInvocationTarget.ReturnType);
			}
			else
			{
				invocation.Proceed();
			}

		}
		private async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation)
		{
			var deleteAttr = (DeleteCacheAttribute)invocation.MethodInvocationTarget.GetCustomAttributes(true).Where(t => t.GetType() == typeof(DeleteCacheAttribute)).FirstOrDefault();
			var cacheAttr = (ReturnValueCacheAttribute)invocation.MethodInvocationTarget.GetCustomAttributes(true).Where(t => t.GetType() == typeof(ReturnValueCacheAttribute)).FirstOrDefault();
			if (deleteAttr != null)
			{
				string deleteKey = AutoFacHelper.GetKey(invocation, deleteAttr);
				cache.DeleteKey(deleteKey);
			}
			if (cacheAttr != null)
			{
				string cacheKey = AutoFacHelper.GetKey(invocation, cacheAttr);
				TResult cacheValue = cache.GetValue<TResult>(cacheKey);
				TResult defaultValue = default(TResult);
				if (Equals(cacheValue, defaultValue))
				{
					lock (locker)
					{
						invocation.Proceed();
						Task<TResult> task = (Task<TResult>)invocation.ReturnValue;
						TResult result = task.Result;//获得返回结果
						cache.SetValue(cacheKey, result, cacheAttr.ExpireMillisecond);
					}
				}
				return cache.GetValue<TResult>(cacheKey);
			}
			else
			{
				//调用业务方法
				invocation.Proceed();
				Task<TResult> task = (Task<TResult>)invocation.ReturnValue;
				TResult result = await task;//获得返回结果
				return result;
			}


		}

		private async Task InternalInterceptAsynchronous(IInvocation invocation)
		{
			var deleteAttr = (DeleteCacheAttribute)invocation.MethodInvocationTarget.GetCustomAttributes(true).Where(t => t.GetType() == typeof(DeleteCacheAttribute)).FirstOrDefault();
			var cacheAttr = (ReturnValueCacheAttribute)invocation.MethodInvocationTarget.GetCustomAttributes(true).Where(t => t.GetType() == typeof(ReturnValueCacheAttribute)).FirstOrDefault();
			if (deleteAttr != null)
			{
				string deleteKey = AutoFacHelper.GetKey(invocation, deleteAttr);
				cache.DeleteKey(deleteKey);
			}
			//没有返回值的方法没必要缓存
			//调用业务方法
			invocation.Proceed();
			Task task = (Task)invocation.ReturnValue;
			await task;//获得返回结果
		}
	}
}
